##------------------------------------------------------------------------------
##
##	yasco-1.0 - Yet Another (Java)Script Obfuscator
##	Copyright (C) 2008-2011 Oliver Schieche <post@oliver-schieche.de>
##
##	Makes use of Yapp
##	Parse::Yapp - Copyright (c) 1998-2001, Francois Desarmenien, all right reserved.
##
##	$Id: Compress3.pm 8 2011-07-07 08:42:03Z goresplatter1844@gmail.com $
##------------------------------------------------------------------------------
package Yasco::Filter::Compress3;

use strict;
use vars qw[@ISA];

use Carp qw/confess/;
use Data::Dumper;
use Yasco::Filter::Result;

use constant MIN_LENGTH		=> 3;
use constant MAX_WINDOW		=> 65535;
use constant MAX_CHUNK_WIDTH => 80;
use constant MAX_LOOK_BACK	=> 1023;
##------------------------------------------------------------------------------
BEGIN {
	require Yasco::Filter;
	@ISA = qw[Yasco::Filter];

	open FILE, '<', __FILE__ or die __FILE__ . ": $!";
	$Yasco::decompressor3 = [map {s/^\s*|\s*$//og;$_} split /^=====/m, (
		[split /^__END__/m, do{local($/); <FILE>}]->[1]
	)];
	close FILE;
}
##------------------------------------------------------------------------------
sub __copy($$$$);
sub tokens {'ZZA_COMPRESS'}
sub max($$) {$_[0] > $_[1] ? $_[0] : $_[1]}
##------------------------------------------------------------------------------
sub new
	{
	    my $self  = shift;
	    my $options = shift;
	    $self = $self->SUPER::new($options);

	    $self->{verbose} ||= 0;
	    $self->{max_block_transform_size} ||= 32;

	    $self
	}
##------------------------------------------------------------------------------
sub execute {}
##------------------------------------------------------------------------------
sub post_process
	{
		my $self = shift;
		my $o = shift;
		my $code = shift;
		my $input = $$code;

		my ($prev,$p,$len,@parts);

		print STDERR "\n" if $self->{verbose};

		$self->init_mtf($input);

		for ($p = 0, $len = length $input; $p <= $len; $p += $self->{max_block_transform_size})
			{
				push @parts, substr($input, $p, $self->{max_block_transform_size});
			}

		printf STDERR "Input length: %d bytes, %d blocks\n\tTransform", $len, 0+@parts if $self->{verbose};

		foreach $p (0..$#parts)
			{
				print STDERR "." if 1 == $self->{verbose};
						__dump($self->{max_block_transform_size} * $p,'BLOCK #'.$p,map {ord} split //, $parts[$p])
							if 2 == $self->{verbose};
				$self->transform_bw(\$parts[$p]);
						__dump(0,'POST-BW',map {ord} @{$parts[$p]})
							if 2 == $self->{verbose};
				$self->transform_mtf(\$parts[$p]);
						__dump(0,'POST-MTF',@{$parts[$p]})
							if 2 == $self->{verbose};
				$self->rle($parts[$p]);
						__dump(0,'POST-RLE',@{$parts[$p]})
							if 2 == $self->{verbose};
						print STDOUT "\n\n" . ('-'x100) . "\n\n"
							if 2 == $self->{verbose};

#				$parts[$p] = [map {
#
#					local($SIG{__WARN__}) = sub {};
#					(0 == $_ and "0" ne $_) ?
#						map {ord} split //
#					:	$_
#
#				} @{$parts[$p]}];
			}

		@parts = map {@$_} @parts;
		$prev = 0+@parts;
		printf STDERR "\nAfter transformation: %d bytes, %.3f%%\n", $prev, 100*($prev/$len)
			if $self->{verbose};

		$$code = $self->encode(@parts);
		printf STDERR "\nAfter encode: %d bytes, %.3f%% / %.3f%% total\n", length $$code, 100*(length($$code) / $prev), 100 * (length($$code) / $len)
			if $self->{verbose};

		$prev = length $$code;
		@parts = split//, $$code;
		$$code = $self->to_jstring(MAX_CHUNK_WIDTH, @parts);

		printf STDERR "After jstring: %d bytes, %.3f%% / %.3f%% total\n", length $$code, 100*(length($$code) / $prev), 100 * (length($$code) / $len)
			if $self->{verbose};

		for ($Yasco::decompressor3->[$self->{debug} || 0]) {

			my $mtf = $self->to_jstring(4+@{$self->{mtf_initialize}}, @{$self->{mtf_initialize}});

			s/#\{CODE\}/$$code/;
			s/#\{MTF_LIST\}/$mtf/;
			s/#\{BLOCK_SIZE\}/$self->{max_block_transform_size}/;

			$$code = $_;
		}

	}
##------------------------------------------------------------------------------
sub init_mtf
	{
		my $self = shift;
		my $code = shift;
		my (%mtf_chars);

		$mtf_chars{$_}=1 foreach split//, $code;

		$self->{mtf_initialize} = ["\xFF", keys %mtf_chars];

		printf STDERR "MTF table initialized w/ %d entries.\n", 1+keys %mtf_chars if $self->{verbose};

		1
	}
##------------------------------------------------------------------------------
sub transform_bw
	{
		my $self = shift;
		my $part = shift;
		my @line = ((split //, $$part), "\xFF");
		my @matrix;

		foreach my $c (0..$#line)
			{
				push @matrix, join('', @line);
				unshift @line, (pop @line);
			}

		$$part = [map {substr $_, -1} sort @matrix];

		1
	}
##------------------------------------------------------------------------------
sub transform_mtf
	{
		my $self = shift;
		my $part = shift;
		my $list = join '', @{$self->{mtf_initialize}};
		my @str = @$$part;
		$$part = [];

		foreach (@str) {
			my $p = index($list, $_);
			confess sprintf("\"%s\" (0x%02X) not found in MTF list???\n%s\n", $_, ord($_), $list) if -1 == $p;
			push @{$$part}, 0x20+$p;
			$list = $_ . substr($list, 0, $p) . substr($list, $p+1);
		}

		1
	}
##------------------------------------------------------------------------------
sub rle
	{
		my $self = shift;
		my $list = shift;
		my ($ch,$n,$len,@res);

		PROCESS_LIST: while (@$list)
			{
				$ch = shift @$list;
				confess "Argh" if "\n" eq $ch;

				push(@res,$ch), next if !@$list or $list->[0] != $ch;
				for ($n = 0, $len = 0+@$list; $n < $len && $list->[$n] == $ch; $n++) {}
				$len = $n;

				SET_MARKER: {

					my ($maxlen) = 0x70;

					push @res, $list->[0];

					if ($len < 0x20)
						{
							next PROCESS_LIST if 2 >= $len;
							push @res, 0x09, 0x20+$len;
							last SET_MARKER
						}
					elsif ($maxlen == $len)
						{
							confess "Handle me";
						}

					my $l = $len;
					push @res, 0x0A;

					$l -= $maxlen,
					push @res, $maxlen while $maxlen < $l;
					push @res, $l+0x20;

#					__dump(0,@res);
#					die Dumper(\@res);
				}

				splice @$list, 0, $len;
			}

		@$list = @res;
	}
##------------------------------------------------------------------------------
sub encode
	{
		my $self = shift;
		my @list = @_;

		my ($c,$dest,$len,$ptr,$offset,$distance,$length,$best_match,$best_length,$scan_ptr,$lastPerc);

		$len		= 0+@list;
		$ptr		= 0;
		$dest		= '';
		$offset		= 0;
		$lastPerc	= 0;

		__copy \@list, \$dest, \$ptr, 4;

		print STDERR "\tEncode:" if $self->{verbose};

		PROCESS_LIST: for(;$ptr < $len;)
			{
				if ($self->{verbose})
					{
						if ($lastPerc != int(100 * $ptr / $len))
							{
								$lastPerc = int(100 * $ptr / $len);
								if (0 == $lastPerc % 5)
									{
										printf STDERR " %d%%", $lastPerc;
									}
							}
					}

				$offset = max 0, $ptr - MAX_LOOK_BACK;
				($best_match,$best_length) = (0,0);

				while ($offset < $ptr)
					{
						last if $ptr + 2 >= $len;

						SCAN_WINDOW: for ($scan_ptr = 0; MAX_WINDOW > $scan_ptr; $scan_ptr++)
							{
								last SCAN_WINDOW if $offset + $scan_ptr == $ptr;
								last SCAN_WINDOW if $list[$offset+$scan_ptr] != $list[$ptr+$scan_ptr];
							}

						if ($best_length <= $scan_ptr)
							{
								$best_length = $scan_ptr;
								$best_match = $offset;
							}

						$offset++;
					}

				if ($best_length <= 2)
					{
						__copy \@list, \$dest, \$ptr, $best_length + 1;
						next
					}

				# distance
				$distance = $ptr - $best_match;
				$length = $best_length;

				BLOCK_FOUND: {
					my ($cl,$c);

					printf STDOUT "*\tBlock of %d bytes \@0x%04X (%d) found \@0x%04X (%d) (distance: %d) ", $best_length, $ptr, $ptr, $best_match, $best_match, $distance
						if 2 == $self->{verbose};

					if (3 == $length)
						{
							$distance += 0x20;
							last BLOCK_FOUND if 0xFF < $distance;
							print STDOUT " [USED]\n"
								if 2 == $self->{verbose};
							$dest .= "\x40" . chr($distance);
							$ptr += 3;
							next PROCESS_LIST;
						}

					$length -= 3;

					if (31 >= $length && 2047 >= $distance)
						{
							last BLOCK_FOUND;

							$distance <<= 5;

							$cl = 3;
							$c = $length | (0xff & $distance);
							$cl += 3 if ($c < 0x20 || $c >= 0x7F);
							$c = $distance >> 8;
							$cl += 3 if ($c < 0x20 || $c >= 0x7F);
							last BLOCK_FOUND if $best_length < $cl;
$distance >>= 5;
					printf STDERR "*\tBlock of %d bytes \@0x%04X (%d) found \@0x%04X (%d) (distance: %d).\n", $best_length, $ptr, $ptr, $best_match, $best_match, $distance;
					printf STDERR "DUMP %X - %X:\n", (($best_match >> 4) << 4),((1 + ($ptr >> 4)) << 4);
					__dump((($best_match >> 4) << 4), 'DUMP', @list[(($best_match >> 4) << 4) .. (-1 + ((2 + ($ptr >> 4)) << 4))]);
					print STDOUT "\n";
$distance <<= 5;

							$dest .= '^' . chr($length | (0xff & $distance)) . chr($distance >> 8);
							__dump(0, 'DEST', map {ord} split//, substr($dest, -3));
							$ptr += $best_length;
							next PROCESS_LIST;
						}

					printf STDERR "*\tBlock of %d bytes \@0x%04X (%d) found \@0x%04X (%d) (distance: %d).\n", $best_length, $ptr, $ptr, $best_match, $best_match, $distance;
					printf STDERR "DUMP %X - %X:\n", (($best_match >> 4) << 4),((1 + ($ptr >> 4)) << 4);
					__dump((($best_match >> 4) << 4), 'DUMP', @list[(($best_match >> 4) << 4) .. (-1 + ((2 + ($ptr >> 4)) << 4))]);
					print STDOUT "\n";
					__dump(0, 'DEST', map {ord} split//, $dest);

					die;
				}

				#printf STDERR "-\tUseless block of %d bytes \@0x%04X (%d) found \@0x%04X (%d) and discarded (distance: %d).\n", $best_length, $ptr, $ptr, $best_match, $best_match, $distance;
				print STDOUT " [DISCARDED]\n"
					if 2 == $self->{verbose};
				__copy \@list, \$dest, \$ptr, $best_length;
			}

		$dest
	}
##------------------------------------------------------------------------------
sub __copy($$$$)
	{
		my ($src,$dest,$offset,$size) = @_;
		my ($c,$end);
		my @args = @_;

		for ($c = $$offset, $end = $$offset+$size; $c < $end; $c++, $$offset++)
			{
				do {
					local($SIG{__WARN__}) = sub
						{
							$_[0] =~ s/\n//;
							__dump( (($c >> 4) << 4), 'DUMP', $src->[ (($c >> 4) << 4) .. 0+@$src ]);
							confess sprintf('%s: offset was %d: %s', $_[0], $c, Dumper(\@args));
						};

					$$dest .= chr($src->[$c]), next
						if	0x40 != $src->[$c] &&
							0x5E != $src->[$c] &&
							0xFF != $src->[$c];
				};

				$$dest .= "\xFF" . chr($src->[$c]);
			}
	}
##------------------------------------------------------------------------------
sub __dump
	{
#		my $data = shift;
#		my @list = map {ord} split //, $data;
		my $off = shift;
		my $caption = shift;
		my @list = @_;
		my ($ptr,$len);

		$len = length $caption;

		print STDOUT "\n----------[ $caption ]";
		print STDOUT ('-' x (58 - $len)) . "\n";
		print STDOUT "     ";
		printf STDOUT "%02X ", $_ foreach (0..7);
		print STDOUT ' ';
		printf STDOUT "%02X ", $_ foreach (8..15);
		print STDOUT ' ';
		printf STDOUT '%X', $_ foreach (0..7);
		print STDOUT ' ';
		printf STDOUT '%X', $_ foreach (8..15);
		print STDOUT "\n";

		for ($len = 0+@list, $ptr = 0; $ptr < $len; $ptr += 16)
			{
				my ($n,$data) = (0,'');
				printf STDOUT "%04X ", $off+$ptr;
				for (; $n < 16 && $ptr + $n < $len; $n++)
					{
						printf STDOUT "%02X ", $list[$ptr+$n];
						$data .= chr($list[$ptr+$n]) if 0x20 <= $list[$ptr+$n] && 0x7F > $list[$ptr+$n];
						$data .= '.' if 0x20 > $list[$ptr+$n] || 0x7F < $list[$ptr+$n];

						$data .= ' ',
						print STDOUT ' ' if 7 == $n;
					}

				while (16 >= ++$n)
					{
						print STDOUT '   ';
						$data .= ' ';

						$data .= ' ',
						print STDOUT ' ' if 7 == $n;
					}

				print STDOUT " $data\n";
			}
	}
##------------------------------------------------------------------------------
sub to_jstring
	{
		my $self = shift;
		my $chunk_width = shift;
		my @parts = @_;
		my @lines;
		my ($p,$c,$line);

		push @lines,'';
		$line = \$lines[-1];

		MK_STRING: while (@parts)
			{
				$p = shift @parts;
				$c = ord $p;

				GET_CHAR: {
					$p = '\n', last GET_CHAR	if 0x0A == $c;
					$p = '\r', last GET_CHAR	if 0x0D == $c;
					$p = '\t', last GET_CHAR	if 0x09 == $c;
					$p = '\\\'', last GET_CHAR	if "'" eq $p;
					$p = '\\\\', last GET_CHAR	if "\\" eq $p;
					last GET_CHAR				if 0x20 <= $c &&
												   0x7F > $c;

					if (@parts && (0x20 >= ord($parts[0]) || 0x7F <= ord($parts[0])))
						{
							$p = shift @parts;
							$p = sprintf('\\u%02X%02X', $c, ord($p));
						}
					else
						{
							$p = sprintf('\\x%02X', $c);
						}
				}

				$$line .= $p;
				next if $chunk_width > length $$line;
				push @lines, '';
				$line = \$lines[-1];
			}

		join "+\n", map {"'$_'"} @lines;
	}
##------------------------------------------------------------------------------
1
__END__
Foo
=====
(function(code,block_size,mtf_list) {

	function decode_mtf(input,mtf)
		{
			var res,i,c,p;

			res = '';

			for (i = 0; i < input.length; i++)
				{
					c = input.charCodeAt(i);

					if (0xFF == c)
						res += input.charAt(++i);
					else
						{
							c -= 0x20;
							if (c >= mtf.length) throw new Error('Invalid MTF index: ' + c + ' (0x' + toHex(c,2) + ')');
							p = mtf.charAt(c);

							res += p;

							mtf = p + mtf.substr(0, c) + mtf.substr(c+1);
						}
				}

			return res;
		}

	function decode_bw(input)
		{
			var i, j, len = input.length, list = new Array();
			if (-1 == input.indexOf("\xFF")) throw new Error('Missing control character in "'+input+'"');
			for (i = 0; i < len; i++) list[i] = '';
			for (i = 0; i < len; i++)
				{
					for (j = 0; j < len; j++)
						{
							list[j] = input.charAt(j) + list[j];
						}
					list = list.sort();
				}
			for (i = 0; i < len; i++)
				if (0xFF == list[i].charCodeAt(0))
					return list[i].substr(1);
			throw new Error("Failed to decode '"+input+"'");
		}

	function decode_rle(input)
		{
			var i, last, len = input.length, res = '';

			for (i = 0; i < len; i++)
				{
					if (0x09 == input.charCodeAt(i))
						{
							var repeat = input.charCodeAt(++i);
							repeat -= 0x20;
							while (repeat--) res += last;
						}
					else if (0x0A == input.charCodeAt(i))
						{
							var repeat = 0;
							var r;

							do {
								r = input.charCodeAt(++i);
								repeat += r;
							} while(0x70 == r);
							repeat -= 0x20;
							while(repeat--) res += last;
						}
					else
						{
							last = input.charAt(i);
							res += last;
						}
				}

			return res;
		}

	var code_length = code.length;
	var i, code_pos, deflated = '', buffer = new Array();

	/*document.write('<pre>');

	function next()
		{
			ch = _next();
			//document.write(ch+"\n");
			return ch;
		}*/

	function next()
		{
			if (buffer.length) return buffer.shift();

			var c = code.charCodeAt(code_pos++);

			if (0xFF < c)
				{
					code = code.substr(0,code_pos) + String.fromCharCode(c&0xFF) + code.substr(code_pos);
					code_length++;
					c >>= 8;
				}

			switch(c)
				{
				case 0x40:
					var pos = deflated.length;
					var distance = next();
					distance = distance.charCodeAt(0);
					if (0xFF < distance)
						{
							if (buffer.length) throw new Error('Non-empty buffer... @'+toHex(code_pos));
							buffer.push(distance & 0xFF);
							distance >>= 8;
						}
					distance -= 0x20;
					pos -= distance;
					buffer.unshift(deflated.charAt(pos+2));
					buffer.unshift(deflated.charAt(pos+1));
					buffer.unshift(deflated.charAt(pos+0));
					return next();

				case 0x5E:
					/*var pos = deflated.length;
					var a = next().charCodeAt(0), b = next().charCodeAt(0);
					var copy_length = 3+(a & 31);
						b &= ~0x1F;
						b <<= 8;
					var from = (a|b)>>5;

					pos -= from;
					while(copy_length--)buffer.push(deflated.charAt(pos++));
					return next();*/
					throw new Error('NOT IMPLEMENTED @'+toHex(code_pos));

				case 0xFF:
					c = code.charCodeAt(code_pos++);
					if (0xFF < c)
						{
							buffer.push(c & 0xFF);
							c >>= 8;
						}
				default:
					return String.fromCharCode(c);
				}
		}

	__dump(code,'PRE-DEFLATE');

	for (code_pos = 0;code_length > code_pos;)
		{
			deflated += next();
		}

	__dump(deflated,'POST-DEFLATE');

	deflated = decode_rle(deflated);

	__dump(deflated,'POST-RLE');

	var chunks = new Array();
	var length = deflated.length;

	// +1 for the control character 0xFF
	for (i = 0; i < length; i += block_size + 1)
		{
			chunks.push(deflated.substr(i, block_size + 1));
		}

	deflated = '';

	for (i = 0, length = chunks.length; length > i; i++)
		{
	__dump(chunks[i], '<h1>Block #'+i+' @'+toHex(block_size * i,4)+'</h1>');
			chunks[i] = decode_mtf(chunks[i],mtf_list);
	__dump(chunks[i],'POST-MTF');
			chunks[i] = decode_bw(chunks[i]);
	__dump(chunks[i],'POST-BW');
		}

	try {
		code = chunks.join('');
		__dump(code);
		document.write('<pre style="white-space:pre-wrap">' + code.escapeHTML() + '</pre>');
		eval(code)
	} catch(E) {
		console.log(E);
		document.write('<pre style="white-space:pre-wrap">' + code + '\n\n' + $H(E).inspect().escapeHTML() + '</pre>');
	}

	function __dump(v,caption)
		{
			var i,c;
			if ('undefined' != ''+caption)document.write('<b>'+caption+'</b>');
			document.write('<table style="white-space:pre;font-family:Lucida Console;font-size:11px" cellspacing="0" cellpadding="2" border="0"><tr><td>&nbsp;</td>');
			for(i=0;i<8;i++)document.write('<td>'+toHex(i,2)+'</td>');
			document.write('<td>&nbsp;</td>');
			for(i=8;i<16;i++)document.write('<td>'+toHex(i,2)+'</td>');
			document.write('<td>&nbsp;|&nbsp;</td><td>');
			for(i=0;i<16;i++)document.write(toHex(i));
			document.write('</td></tr>');

			var ul = 0;

			for(i=0,l=v.length;i<l;i+=16)
				{
					document.write('<tr><td>'+toHex(i,4)+'</td>');
					data = '';
					for(c=0;c<16;c++)
						{
							if (i+c < l)
								{
									var cc = v.charCodeAt(i+c);

									if (0xFF < cc)
										{
											ul = 2;
											v = v.substr(0,i+c+1) + String.fromCharCode(cc&0xFF) + v.substr(i+c+1);
											cc >>= 8;
										}

									cell = toHex(cc,2);
									if (0x20 == cc) cell = '<span style="color:#999">'+cell+'</span>';
									if (0x0A == cc) cell = '<span style="color:#C00">'+cell+'</span>';
									if (0x09 == cc) cell = '<span style="color:#C00">'+cell+'</span>';
									if (0x40 == cc) cell = '<span style="color:#00C">'+cell+'</span>';
									if (0xFF == cc) cell = '<span style="color:#C0C">'+cell+'</span>';

									if (ul)
										{
											cell = '<u style="border:1px solid red">'+cell+'</u>';
											ul--;
										}

									if (0x20 > cc || 0x7F <= cc)
										ds = '.';
									else if (0x3C == cc)
										ds = '&lt;';
									else
										ds = String.fromCharCode(cc);
								}
							else
								{
									cell = '&nbsp;';
									ds = '&nbsp;';
								}

							document.write('<td>'+cell+'</td>');

							data += ds;
							if (7 == c)
								{
									document.write('<td>&nbsp;</td>');
								}
						}

					document.write('<td>&nbsp;|&nbsp;</td><td>'+data+'</td>');
					document.write('</tr>');
				}

			document.write('</table><hr />');
		}

	function toHex(n,pad)
		{
			var digits = '0123456789ABCDEF';
			var res = '';

			do
				{
					res = digits.charAt(n&0xF) + res;
					n >>= 4;
				}
			while (n);

			if ('undefined' != ''+pad)
				while (pad > res.length) res = '0' + res;

			return res;
		}

})(#{CODE},#{BLOCK_SIZE},#{MTF_LIST});
=====
eval((function(code,block_size,mtf_list) {
	String.prototype._9 = function(_0) {return this.charCodeAt(_0)};
	String.prototype._8 = function(_0) {return this.substr(_0)};
	String.prototype._7 = function(_0) {return this.charAt(_0)};
	String.prototype._6 = function(_0,_1) {return this.substr(_0,_1)};

	function decode_mtf(input,mtf)
		{
			var res,i,c,p;

			res = '';

			for (i = 0; i < input.length; i++)
				{
					c = input._9(i);

					if (0xFF == c)
						res += input._7(++i);
					else
						{
							c -= 0x20;
							p = mtf._7(c);

							res += p;

							mtf = p + mtf._6(0, c) + mtf._8(c+1);
						}
				}

			return res;
		}

	function decode_bw(input)
		{
			var i, j, len = input.length, list = new Array();
			for (i = 0; i < len; i++) list[i] = '';
			for (i = 0; i < len; i++)
				{
					for (j = 0; j < len; j++)
						{
							list[j] = input._7(j) + list[j];
						}
					list = list.sort();
				}
			for (i = 0; i < len; i++)
				if (0xFF == list[i]._9(0))
					return list[i]._8(1);
		}

	function decode_rle(input)
		{
			var i, last, len = input.length, res = '';

			for (i = 0; i < len; i++)
				{
					if (0x09 == input._9(i))
						{
							var repeat = input._9(++i);
							repeat -= 0x20;
							while (repeat--) res += last;
						}
					else if (0x0A == input._9(i))
						{
							var repeat = 0;
							var r;

							do {
								r = input._9(++i);
								repeat += r;
							} while(0x70 == r);
							repeat -= 0x20;
							while(repeat--) res += last;
						}
					else
						{
							last = input._7(i);
							res += last;
						}
				}

			return res;
		}

	var code_length = code.length;
	var i, code_pos, deflated = '', buffer = new Array();

	/*document.write('<pre>');

	function next()
		{
			ch = _next();
			//document.write(ch+"\n");
			return ch;
		}*/

	function next()
		{
			if (buffer.length) return buffer.shift();

			var c = code._9(code_pos++);

			if (0xFF < c)
				{
					code = code._6(0,code_pos) + String.fromCharCode(c&0xFF) + code._8(code_pos);
					code_length++;
					c >>= 8;
				}

			switch(c)
				{
				case 0x40:
					var pos = deflated.length;
					var distance = next();
					distance = distance._9(0);
					if (0xFF < distance)
						{
							buffer.push(distance & 0xFF);
							distance >>= 8;
						}
					distance -= 0x20;
					pos -= distance;
					buffer.unshift(deflated._7(pos+2));
					buffer.unshift(deflated._7(pos+1));
					buffer.unshift(deflated._7(pos+0));
					return next();

				case 0xFF:
					c = code._9(code_pos++);
					if (0xFF < c)
						{
							buffer.push(c & 0xFF);
							c >>= 8;
						}
				default:
					return String.fromCharCode(c);
				}
		}

	for (code_pos = 0;code_length > code_pos;)
		{
			deflated += next();
		}

	deflated = decode_rle(deflated);

	var chunks = new Array();
	var length = deflated.length;

	// +1 for the control character 0xFF
	for (i = 0; i < length; i += block_size + 1)
		{
			chunks.push(deflated._6(i, block_size + 1));
		}

	deflated = '';

	for (i = 0, length = chunks.length; length > i; i++)
		{
			chunks[i] = decode_mtf(chunks[i],mtf_list);
			chunks[i] = decode_bw(chunks[i]);
		}

	try {
		code = chunks.join('');
		document.write('<pre style="white-space:pre-wrap">' + code.escapeHTML() + '</pre>');
		return(code)
	} catch(E) {
		console.log(E);
		document.write('<pre style="white-space:pre-wrap">' + code + '\n\n' + $H(E).inspect().escapeHTML() + '</pre>');
	}

})(#{CODE},#{BLOCK_SIZE},#{MTF_LIST}));